package cn.slg.module.system.dal.dataobject.wujiang;

import cn.slg.module.system.dal.dataobject.group.Group;
import cn.slg.module.system.dal.dataobject.state.PersistenceState;
import cn.slg.module.system.dal.dataobject.state.PropertyState;
import cn.slg.module.system.dal.dataobject.state.functional.FunctionalState;
import cn.slg.module.system.service.pk.skill.SKillCount;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.*;
import java.util.function.Supplier;

import static cn.slg.module.system.service.pk.PKServiceImpl.activeSkill;
import static cn.slg.module.system.service.pk.utils.Die.dieProcess;
import static cn.slg.module.system.service.pk.utils.Die.whetherContinue;
import static cn.slg.module.system.service.pk.utils.DieAndRescue.harmProcess;
import static cn.slg.module.system.service.pk.utils.Harm.harm;
import static cn.slg.module.system.service.pk.utils.Show.*;
import static cn.slg.module.system.service.pk.utils.Target.randomTargets;

@TableName(value = "wu_jiang")
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder    // 开启创建对象的Builder模式
public class WuJiangDO {

    // 存放着队友
    @TableField(exist = false)
    private Group teammates;

    // 存放着敌人
    @TableField(exist = false)
    private Group enemy;

    // 该武将是否已经战死
    @TableField(exist = false)
    private Boolean isDie = false;

    // 武将身上存在的所有状态
    // 所有的属性状态，
    // 被施加者死了、施加者死了、这些属性状态
    @TableField(exist = false)
    private List<PropertyState> propertyStates = new ArrayList<>();

    // 被施加的持续性状态，只有急救是受到伤害时触发，其他都是行动时触发
    // 武将如果死了，这些持续性状态也就失效了。
    @TableField(exist = false)
    private List<PersistenceState> persistenceStates = new ArrayList<>();

    // 被施加的功能性状态
    @TableField(exist = false)
    private List<FunctionalState> functionalStates = new ArrayList<>();


    // 三个技能
    private String firstSkill;
    private String secondSkill;
    private String thirdSkill;
    // 三个技能的等级（1~10）
    private Byte firstSkillLevel;
    private Byte secondSkillLevel;
    private Byte thirdSkillLevel;

    // 统计所有战法的伤害，
    @TableField(exist = false)
    private Map<String, SKillCount> skillCount = new LinkedHashMap<>();

    // 准备回合战法发动顺序
    @TableField(exist = false)
    private List<String> prepareSkillOrder;

    // 战斗回合战法发动顺序
    @TableField(exist = false)
    private List<String> fightSkillOrder;

    // 记录正在准备的战法，下一回合发动，
    // 如果下一回合发动战法前，处于技穷状态，记得把这些准备战法清空，避免在技穷结束后发动
    @TableField(exist = false)
    private List<String> preparingSkill = new ArrayList<>();

    // 主动战法发动几率加成，默认是0
    @TableField(exist = false)
    private Float initiativeSkillRate = 0f;

    // 士气伤害倍率，小数。准备回合基于士气确定。
    @TableField(exist = false)
    private Float harmRateByShiQi = 1f;

    // 普攻及兵刃伤害战法会造成兵刃伤害，武力属性越高则伤害越高。提高统帅属性可减少武将受到的兵刃伤害
    // 兵刃伤害倍率（0~），默认为1.兵刃伤害增加就提升，减少就降低
    // 自己、队友的增益状态和敌人的 debuff 都会影响。
    // 造成兵刃伤害时： 兵刃伤害倍率 * 伤害数值
    @TableField(exist = false)
    private Float physicalHarmRate = 1f;

    // 谋略伤害战法会造成谋略伤害，智力属性越高则伤害越高。提高智力属性也可以减少受到的谋略伤害。
    // 谋略伤害倍率（0~），默认为1.谋略伤害增加就提升，减少就降低
    // 造成谋略伤害时： 谋略伤害倍率 * 伤害数值
    @TableField(exist = false)
    private Float magicHarmRate = 1f;

    // 受到兵刃伤害时，减伤倍率。（0~）
    // 默认是100%。受到伤害增加就上升，减伤就下降。
    @TableField(exist = false)
    private Float isPhysicalHarmRate = 1f;

    // 受到谋略伤害时，减伤倍率。（0~）
    // 默认是100%。受到伤害增加就上升，减伤就下降。
    @TableField(exist = false)
    private Float isMagicHarmRate = 1f;


    // 该武将累计 普攻杀敌
    @TableField(exist = false)
    private Integer totalAttackHarm = 0;

    // 该武将累计 战法杀敌
    @TableField(exist = false)
    private Integer totalSkillHarm = 0;

    // 该武将累计 救援
    @TableField(exist = false)
    private Integer totalCure = 0;

    // 该武将 死兵 (即死兵，受到伤害时，10%变死兵；新一回合，10%的伤兵变死兵，战斗结束还会变死兵)
    @TableField(exist = false)
    private Integer totalDie = 0;

    // 该武将的 损失（即受到的伤害值的累计）
    @TableField(exist = false)
    private Integer totalLoss = 0;

    // 该武将 本场战斗的伤兵
    @TableField(exist = false)
    private Integer totalWounded = 0;

    // 该武将 累计伤兵
    // 每场战斗后，产生的伤兵，不会被带入下一场战斗，只有回到主城、营长，才会将伤兵加到兵力中。
    private Integer accumWounded;

    // 武将唯一id
    @TableId
    private Integer id;

    private Integer userId;

    // 武将名字
    private String name;

    // 武将品质
    private String wuJiangQuality;

    // 武将统御
    private Byte wuJiangTongYu;

    // 武将阵营（魏、蜀、吴、群）
    private String wuJiangZhenYing;

    // 武将类型（军事、内政）
    private String wuJiangLeiXing;

    // 等级 0~50
    private Byte level;

    // 经验
    private Float experience;

    // 体力
    private Short tiLi;

    // 兵力
    private Short bingLi;


    // 武将总属性值 = 基础属性+（等级+属性加点）*属性倍率
    private Float pvWuLi;
    private Float pvTongShuai;
    private Float pvZhengZhi;
    private Float pvZhiLi;
    private Float pvSuDu;
    private Float pvMeiLi;

    // 武将基础属性值
    private Float pbWuLi;
    private Float pbTongShuai;
    private Float pbZhengZhi;
    private Float pbZhiLi;
    private Float pbSuDu;
    private Float pbMeiLi;

    // 武将各属性加点 0~100(50级+5红)
    private Byte paWuLi;
    private Byte paTongShuai;
    private Byte paZhengZhi;
    private Byte paZhiLi;
    private Byte paSuDu;
    private Byte paMeiLi;

    // 武将属性加点倍率，即每加一点，会加多少属性。
    private Float pmWuLi;
    private Float pmTongShuai;
    private Float pmZhengZhi;
    private Float pmZhiLi;
    private Float pmSuDu;
    private Float pmMeiLi;

    // 兵种适性
    private String qiShiXing;
    private String dunShiXing;
    private String gongShiXing;
    private String qiangShiXing;
    private String qiXieShiXing;

    @TableField(exist = false)
    private String bingZhong;

    // 判断自己能否行动
    public boolean canAction() {
        // TODO 是否处于震慑状态
        // TODO 判断是否处于 震慑 状态，即无法发动主动战法和普攻，但是能触发被动。

        return true;
    }

    // 判断自己能否发动普通攻击
    public boolean canNormalAttack() {
        // TODO 是否处于 缴械 状态，无法发动普通攻击。

        return true;
    }

    // 判断自己能否发动战法
    public boolean canSkillAttack() {
        // TODO 可以在这先判定 canAction()
        // TODO 是否处于 计穷 状态，即无法发动主动战法。
        return true;
    }


    // 该武将行动回合
    public boolean whenAction(byte roundNumber) {
        boolean canAction = true;

        // 首先处理各种状态，比如受到的治疗和伤害。
        // TODO 行动开始时触发的状态：灼烧、水攻、中毒、溃逃、沙暴、叛逃、休整
        if (!this.persistenceStates.isEmpty()) {
            // 遍历这些状态
            for (int i = 0; i < persistenceStates.size(); i++) {
                PersistenceState persistenceState = persistenceStates.get(i);
                if (!persistenceState.getStateName().equals("急救")) {
                    // 非急救状态，在这里触发，返回是否由于该状态的触发，导致战斗结束
                    boolean isEnd = persistenceState.effect();
                    // 如果战斗结束了，或者该武将死了
                    if (isEnd || isDie) {
                        canAction = false;
                        break;
                    }
                }
            }
        }

        // 如果可以行动，即战斗没有结束，且自己没有死亡，就发动指挥战法
        if (canAction) {
            // 依次发动指挥战法
            List<String> prepareSkillOrder = this.prepareSkillOrder;
            for (int j = 0; j < prepareSkillOrder.size(); j++) {
                String prepareSkill = prepareSkillOrder.get(j);
                if (prepareSkill.startsWith("指挥_")) {
                    // TODO 伪报和捕获状态，会使该武将的指挥战法失效。所以发动每个指挥战法之前，都要判定下是否处于这两个状态。

                    if (true) {
                        // TODO 发动该指挥战法，
                        activeSkill(this, prepareSkill, roundNumber);

                        // 发动一个指挥战法，就进行校验，战斗是否继续
                        // 判定战斗是否结束,结束了,就没必要继续走下去了
                        // 判定这个武将是否死了，死了就不继续发动指挥战法了。
                        if (!whetherContinue(this) || isDie) {
                            canAction = false;
                            break;
                        }
                    }
                }
            }
        }
        return canAction;
    }


    // TODO 受到普通攻击或战法攻击时触发(无论是否受到伤害)
    public void whenIsAttack() {

    }

    // TODO 受到普通攻击时触发
    public boolean whenIsNormalAttacked(WuJiangDO attacker, Short harm) {
        boolean isEnd = false;

        // TODO 受到攻击时要执行：援护、分摊、分担、铁索连环
        harm = whenIsHarmed(harm);

        // 伤害判定机制(伤害数值\是否战死\是否结束战斗)
        // 如果伤害超过了剩余兵力
        if (harm > this.bingLi) {
            // 1.先将伤害 = 剩余兵力
            harm = this.bingLi;

            // 进行数值操作：基于伤害扣除目标兵力，并将伤害的 90% 转化为伤兵，伤害的 10% 直接变死兵
            harmProcess(this, harm);

            // 显示信息
            normalAttackHarmShow(this, harm);

            // 2.设置该武将战死
            this.setIsDie(true);

            // 3.进行战死处理
            isEnd = dieProcess(attacker, this);
        } else {

            // 基于伤害扣除目标兵力，并将伤害的 90% 转化为伤兵，伤害的 10% 直接变死兵
            harmProcess(this, harm);

            // 显示信息
            normalAttackHarmShow(this, harm);

            // TODO 如果自己没有死：急救、
            persistenceStates.forEach(persistenceState -> {
                if (persistenceState.getStateName().equals("急救")) {
                    // TODO 执行急救状态
                    persistenceState.effect();
                }
            });

            // TODO 触发自己的：反击
        }
        return isEnd;
    }

    // TODO 受到持续性状态伤害时触发
    public boolean whenIsStateAttack(WuJiangDO attacker, String skillName, String stateName, Short harm) {
        boolean isEnd = false;

        // TODO 受到攻击时要执行：援护、分摊、分担、铁索连环，影响对目标的伤害值
        harm = whenIsHarmed(harm);
        // 伤害判定机制(伤害数值\是否战死\是否结束战斗)
        // 如果伤害超过了剩余兵力
        if (harm > this.bingLi) {
            // 1.先将伤害 = 剩余兵力
            harm = this.bingLi;

            // 进行数值操作：基于伤害扣除目标兵力，并将伤害的 90% 转化为伤兵，伤害的 10% 直接变死兵
            harmProcess(this, harm);

            // 显示信息
            stateHarmShow(attacker, this, skillName, stateName, harm);

            // 2.设置该武将战死
            this.setIsDie(true);

            // 3.进行战死处理
            isEnd = dieProcess(attacker, this);
        } else {

            // 进行数值操作：基于伤害扣除目标兵力，并将伤害的 90% 转化为伤兵，伤害的 10% 直接变死兵
            harmProcess(this, harm);

            // 显示信息
            stateHarmShow(attacker, this, skillName, stateName, harm);

            // TODO 如果自己没有死：急救、
            persistenceStates.forEach(persistenceState -> {
                if (persistenceState.getStateName().equals("急救")) {
                    // TODO 执行急救状态
                    persistenceState.effect();
                }
            });

            // TODO 触发自己的：反击
        }
        return isEnd;
    }

    // TODO 受到战法攻击时触发
    public boolean whenIsSkillAttack(WuJiangDO attacker, String skillName, Short harm) {
        boolean isEnd = false;

        // TODO 受到攻击时要执行：援护、分摊、分担、铁索连环
        harm = whenIsHarmed(harm);
        // 伤害判定机制(伤害数值\是否战死\是否结束战斗)
        // 如果伤害超过了剩余兵力
        if (harm > this.bingLi) {
            // 1.先将伤害 = 剩余兵力
            harm = this.bingLi;

            // 进行数值操作：基于伤害扣除目标兵力，并将伤害的 90% 转化为伤兵，伤害的 10% 直接变死兵
            harmProcess(this, harm);

            // 显示信息
            skillHarmShow(attacker, this, skillName, harm);

            // 2.设置该武将战死
            this.setIsDie(true);

            // 3.进行战死处理
            isEnd = dieProcess(attacker, this);
        } else {

            // 进行数值操作：基于伤害扣除目标兵力，并将伤害的 90% 转化为伤兵，伤害的 10% 直接变死兵
            harmProcess(this, harm);

            // 显示信息
            skillHarmShow(attacker, this, skillName, harm);

            // TODO 如果自己没有死：急救、
            persistenceStates.forEach(persistenceState -> {
                if (persistenceState.getStateName().equals("急救")) {
                    // TODO 执行急救状态
                    persistenceState.effect();
                }
            });
            // TODO 触发自己的：反击
        }
        return isEnd;

    }


    // TODO 受到伤害时触发，返回调整后的伤害值
    public Short whenIsHarmed(Short harm) {
        // TODO 目标要执行：援护、分摊、分担、铁索连环

        return harm;
    }

    /**
     * 确定目标选择范围，基于自身的状态
     *
     * @param teammate 是不是要选择队友
     * @return 可选范围
     */
    public List<WuJiangDO> confirmChoiceRange(boolean teammate) {
        List<WuJiangDO> choiceRange;
        // TODO 这里触发各种影响目标选定的状态：
        // TODO 这里触发：群攻（可选择多个目标）、穷追（普通攻击锁定目标）、混乱（攻击和战法无差别选择目标）、嘲讽（强迫普通攻击某一目标）
        // TODO 挑拨（强迫战法攻击某一目标）
        if (teammate) {
            choiceRange = this.teammates.getFightGroup();
        } else {
            choiceRange = this.enemy.getFightGroup();
        }
        return choiceRange;
    }


    // TODO 判断能否命中目标时触发
    public boolean whetherHitTarget(WuJiangDO target) {
        // TODO 攻击者向目标发动攻击后触发：规避、必中（无视目标的规避或抵御）
        // 如果自己有必中或者目标没有规避，则命中目标。
        // 否则，没有命中目标

        // TODO 如果是队友，队友能不能接收buff加成
        return true;
    }


    // TODO 发动普通攻击时触发
    public void whenNormalAttack(WuJiangDO target, Short harm) {

        // 先执行因为普通攻击造成兵刃伤害，触发的效果
        whenMakeWeaponHarm(target, harm);

        // 攻击后触发的效果
        afterNormalAttack(target, harm);
    }

    // TODO 攻击者发动普通攻击后触发
    public void afterNormalAttack(WuJiangDO target, Short harm) {
        // TODO 每次普通攻击后都会触发的东西：
        // TODO 比如触发队友（曹操的乱世奸雄：如果自己为主将，副将造成伤害时，会为主将恢复其伤害量10%的兵力。）
        // TODO 或敌人的状态效果
        // TODO 或自己的：穷追(选中该目标)、属性状态（突击战法发动几率、）

        // TODO 处理突击
        // 先判定有没有突击战法
        // 再判定攻击者可不可以发动突击，如果处于缴械、震慑等状态，就无法发动（攻击者攻击目标，可能触发缴械等负面效果，所以需要再次在这里判定）。
        // 再遍历该武将的突击战法，在这里依次调用
        // 建议把突击战法和普通攻击分开，避免出现死循环递归

        if (true) {
            // 有突击战法
            if (true) {
                // 可以发动突击战法
                // 遍历拿到突击战法
                for (String skillName : fightSkillOrder) {
                    if (skillName.startsWith("突击_")) {
                        whenHaveContinuousAttack(skillName);
                    }
                }
            }
        }

    }

    // 发动突击战法
    public void whenHaveContinuousAttack(String skillName) {
    }


    // TODO (准备发动)主动战法时触发
    public void whenPrepareSkill() {

    }

    // TODO (发动)主动战法，且是兵刃攻击时触发
    public void whenSkillPhysicalAttack(WuJiangDO target, Short harm) {

        // 先执行造成兵刃伤害，触发的效果
        whenMakeWeaponHarm(target, harm);

        // 攻击后触发的效果

    }


    // TODO (发动)主动战法，且是谋略攻击时触发
    public void whenSkillTrickAttack(WuJiangDO target, Short harm) {

        // 先执行造成谋略伤害，触发的效果
        whenMakeTrickHarm(target, harm);

        // 攻击后触发的效果

    }


    /**
     * TODO 造成伤害时触发，计算伤害值
     *
     * @param attacker
     * @param target
     * @param getSelfPv
     * @param getTargetPv
     * @param harmRate    伤害系数，普攻是100%。技能按照技能面板
     * @return 伤害值
     */
    public Short whenCalculateHarm(boolean isPhysical, WuJiangDO attacker, WuJiangDO target, Supplier<Float> getSelfPv, Supplier<Float> getTargetPv, Float harmRate) {
        // 伤害值
        Short harm;

        // 是否被抵御
        boolean wsResisted = false;
        // TODO 抵御(可免疫伤害)、必中（无视抵御，仍造成伤害）
        // 如果自己有必中或者目标没有抵御，则正常造成伤害。
        // 否则，伤害值设置为0，但是仍能够触发各种效果。

        // 计算伤害值，普通攻击造成伤害值，伤害系数按照 100%，即1.0f。
        if (wsResisted) {
            harm = 0;
        } else {
            // TODO 虚弱：伤害值直接是0。
            boolean selfIsWeak = false;
            if (selfIsWeak) {
                harm = 0;
            } else {
                // TODO 这里触发攻击者和目标的各种属性加成状态。

                // TODO 破阵：攻击者造成伤害时无视目标统率及智力

                harm = harm(isPhysical, attacker, target, attacker::getPvWuLi, target::getPvTongShuai, harmRate);

                // TODO 触发目标的警戒（可减少受到的伤害）：

            }
        }
        return harm;
    }

    // TODO 造成兵刃伤害时触发
    public void whenMakeWeaponHarm(WuJiangDO target, Short harm) {
        // TODO 攻击者执行：倒戈（造成兵刃伤害时，根据伤害量恢复自身一定兵力。）

    }

    // TODO 造成谋略伤害时触发
    public void whenMakeTrickHarm(WuJiangDO target, Short harm) {

    }

    // TODO 恢复兵力时触发
    public void whenRecover() {

    }


    // 普通攻击， 攻击者调用这个函数
    // 因为每个武将都有普通攻击，所以放在这个类，作为一个普通函数
    public boolean normalAttack() {
        boolean isEnd = false;
        // 可选目标范围
        List<WuJiangDO> confirmChoiceRange = confirmChoiceRange(false);

        // 普攻，在可选范围内，随机选择一个目标
        List<WuJiangDO> attackTargets = randomTargets(confirmChoiceRange, 1);

        // 如果可以选择目标，就遍历这些目标
        if (!attackTargets.isEmpty()) {
            for (WuJiangDO target : attackTargets) {
                // 显示[攻击者]对[目标发动普通攻击]
                showNormalAttack(this, target);

                // 先看能否命中目标
                boolean whetherHitTarget = whetherHitTarget(target);

                if (whetherHitTarget) {
                    // 如果命中目标

                    // harm>=0，非null，返回伤害值。这里确定了伤害值
                    Short harm = whenCalculateHarm(true, this, target, this::getPvWuLi, target::getPvTongShuai, 1.0f);

                    // 累计 攻击者 普攻杀敌(上面已经计算好此次攻击的伤害值了，无论目标死没死，这都要先统计)
                    this.totalAttackHarm += harm;

                    // 先处理受攻击者(返回是否战斗结束)
                    isEnd = target.whenIsNormalAttacked(this, harm);

                    // 战斗是否结束了
                    if (!isEnd) {
                        // 如果没有结束战斗，攻击者执行。。。
                        this.whenNormalAttack(target, harm);
                    } else {
                        // 4.如果要结束战斗,就不触发攻击者的各种效果，也不继续针对下一个目标发动攻击了
                        break;
                    }
                } else {
                    // 如果没有命中目标

                }
            }
        }

        return isEnd;
    }


}

